///////////////////////////////////////////////////////////////////////////
//
// Spring Engine Source File.
// Copyright (C), Dengtao 2011-2021
// ----------------------------------------------------------------------
// File name:   SfGameObject.cpp
// Version:     v1.0.0
// Created:     2013/06/16 16:06 by dengtao
// Compilers:   Visual Studio 2008
// Description: 
// ----------------------------------------------------------------------
// History:
//
///////////////////////////////////////////////////////////////////////////

#include "SfGameObject.h"
#include "SfGameObjectManager.h"
#include "SfGameObjectComponent.h"
#include <algorithm>

namespace Sunflower
{
//
//Summary:
//	constructor.
GameObject::GameObject()
	: m_GoName("")
{
	if (SfIsValidPtr(GameObjectManager::GetInstancePtr()))
	{
		GameObjectManager::GetInstancePtr()->Register(this);
	}
}

GameObject::GameObject(const char* name)
{
	SetGoName(name);

	if (SfIsValidPtr(GameObjectManager::GetInstancePtr()))
	{
		GameObjectManager::GetInstancePtr()->Register(this);
	}
}

//
//Summary:
//	destructor.
GameObject::~GameObject()
{
	RemoveAllGocs();

	if (SfIsValidPtr(GameObjectManager::GetInstancePtr()))
	{
		GameObjectManager::GetInstancePtr()->Unregister(this);
	}
}

//
//Summary:
//	Add goc.
bool GameObject::AddGoc(GameObjectComponent* goc)
{
	if (!SfIsValidPtr(goc))
	{
		return false;
	}

	GameObject* old_host = goc->GetHost();

	if (old_host == this)
	{
		return false;
	}

	// add to vector.
	m_Gocs.push_back(goc);

	if (SfIsValidPtr(old_host))
	{
		old_host->RemoveGoc(goc);
	}

	OnAddGoc(goc);
	RaiseAddGocEvent(goc);

	// goc attach
	goc->Attach(this);

	return true;
}

//
//Summary:
//	Remove goc.
bool GameObject::RemoveGoc(GameObjectComponent* goc)
{
	GocVectorIterator it = std::find(m_Gocs.begin(),
		m_Gocs.end(), goc);

	if (it != m_Gocs.end())
	{
		OnRemoveGoc(goc);
		RaiseRemoveGocEvent(goc);
				
		goc->Detach();

		m_Gocs.erase(it);

		return true;
	}

	return false;
}

//
//Summary:
//	Remove all gocs.
void GameObject::RemoveAllGocs(void)
{
	OnRemoveAllGocs();
	RaiseRemoveAllGocsEvent();

	for (GocVectorIterator it = m_Gocs.begin();
		 it != m_Gocs.end();
		 ++it)
	{
		GameObjectComponent* goc = *it;
		SfAssertValidPtr(goc);

		OnRemoveGoc(goc);
		RaiseRemoveGocEvent(goc);

		goc->Detach();
	}

	m_Gocs.clear();
}

//
//raise events.
void GameObject::RaiseAddGocEvent(GameObjectComponent* goc)
{
	eventAddGoc(this, goc);
}

void GameObject::RaiseRemoveGocEvent(GameObjectComponent* goc)
{
	eventRemoveGoc(this, goc);
}

void GameObject::RaiseRemoveAllGocsEvent()
{
	eventRemoveAllGocs(this);
}

//
//Summary:
//	callback on goc name changing.
//	return true if name can change, return false if name conflict.
bool GameObject::OnGoNameChanging(GameObjectComponent* goc, 
	const char* newName)
{
	SfUnreferencedParam(goc);
	return SfIsNullPtr(FindGoByName(newName));
}

//
//Summary:
//	callback on add goc.
void GameObject::OnAddGoc(GameObjectComponent* goc)
{
	SfUnreferencedParam(goc);
}

//
//Summary:
//	callback on remove goc.
void GameObject::OnRemoveGoc(GameObjectComponent* goc)
{
	SfUnreferencedParam(goc);
}

//
//Summary:
//	callback on remove all gocs.
void GameObject::OnRemoveAllGocs()
{
}

//
//Summary:
//	find goc by name, just search at level 1.
GameObjectComponent* GameObject::FindGoByName(const char* name)
{
	GameObjectComponent* goc = SfNull;

	for (GocVectorIterator it = m_Gocs.begin();
		 it != m_Gocs.end();
		 ++it)
	{
		if (SfIsValidPtr(*it))
		{
			goc = (*it)->GetGocByName(name);

			if (SfIsValidPtr(goc))
			{
				return goc;
			}
		}
	}

	return SfNull;
}

//
//Summary:
//	find goc by id, just search at level 1.
GameObjectComponent* GameObject::FindGoByID(const GameObjectGUID& goid)
{
	GameObjectComponent* goc = SfNull;

	for (GocVectorIterator it = m_Gocs.begin();
		it != m_Gocs.end();
		++it)
	{
		goc = *it;

		if (SfIsValidPtr(goc))
		{
			if (goc->GetGoid() == goid)
			{
				return goc;
			}
		}
	}

	return SfNull;
}

//
//Summary:
//	retrieves count of gocs.
unsigned int GameObject::GetGocCount() const
{
	return (unsigned int)m_Gocs.size();
}

//
//Summary:
//	access goc at given positon.
GameObjectComponent* GameObject::GetGocAt(unsigned int pos)
{
	if (pos >=0 && pos < GetGocCount())
	{
		return m_Gocs[pos];
	}

	return SfNull;
}

//
//Summary:
//	override IMessageHandler::OnMessage() to handle message.
MessageHandleResult GameObject::OnMessage(const Message& msg)
{
	SfUnreferencedParam(msg);
	return MessageHandleResultDone;
}

}
